<?php
if(!interface_exists('XCriteriaTranslator', false)){
	require(XFRAMEWORK_DIR.'/module/orm/criteria/XCriteriaTranslator.php');
}
class XMongoCriteriaTranslator implements XCriteriaTranslator{
	protected $driver = null;
	
	public function setDriver($driver){
		$this->driver = $driver;
	}
	
	public function translate(XTable $table, XCriteria $criteria){
		$cursor = $this->driver->collection($table);
		$cursor = $cursor->find($this->translateCriteria($table, $criteria));
		$projections = $criteria->projections();
		if(count($projections) == 0){
			return $cursor;
		}
		if(isset($projections[ORDER])){
			$orders = array();
			foreach($projections[ORDER] as $projection){
				$asc = $projection->alias();
				$orders[$projection->value()] = $asc ? 1 : -1;
			}
			$cursor = $cursor->sort($orders);
		}
		if(isset($projections[SIZE])){
			$cursor = $cursor->limit($projections[SIZE]->value());
		}
		if(isset($projections[OFFSET])){
			$cursor = $cursor->skip($projections[OFFSET]->value());
		}				
		if(isset($projections[RESULT])){
			$value = $projections[RESULT]->value();
			$fields = explode(',', $value);
			$fields = array_combine(array_values($fields), array_fill(0, count($fields), true));
			$cursor = $cursor->fields($fields);
			return $cursor;
		}
		if(isset($projections[COUNT])){
			return $cursor->count(true);
		}
		if(isset($projections[SUM])){
			throw new XORMException("SUM is not supported by MongoDB!");
		}
		if(isset($projections[AVG])){
			throw new XORMException("AVG is not supported by MongoDB!");
		}		
		return $cursor;
	}
	
	protected function translateCriteria(XTable $table, XCriteria $criteria){
		$queries = array();
		$criterias = array();
		foreach($criteria->criterias() as $key => $criterion){
			if(XClassLoader::loader($criterion)->hasAncestor('XCriteria')){
				$criterias[] = $criterion;
				continue;
			}			
			switch($criterion->type()){
				case EQUAL:
					$queries = x_array_set($queries, $criterion->field(), $criterion->value());
					break;
				case NOT_EQUAL:
					$queries = x_array_set($queries, $criterion->field(), array('$ne' => $criterion->value()));
					break;
				case GREATER:
					$queries = x_array_set($queries, $criterion->field(), array('$gt' => $criterion->value()));
					break;
				case NOT_GREATER:
					$queries = x_array_set($queries, $criterion->field(), array('$lte' => $criterion->value()));
					break;	
				case LESS:
					$queries = x_array_set($queries, $criterion->field(), array('$lt' => $criterion->value()));
					break;
				case NOT_LESS:
					$queries = x_array_set($queries, $criterion->field(), array('$gte' => $criterion->value()));
					break;	
				case IN:
					$queries = x_array_set($queries, $criterion->field(), array('$in' => $criterion->value()));
					break;
				case NOT_IN:
					$queries = x_array_set($queries, $criterion->field(), array('$nin' => $criterion->value()));
					break;
				case LIKE:
					$value = addslashes($criterion->value());
					switch($criterion->alias()){
						case LIKE_ANYWHERE:
							$value = '.*'.$value.'.*';
							break;
						case LIKE_START:
							$value = '^'.$value.'.*';
							break;
						case LIKE_END:
							$value = '.*'.$value.'$';
							break;
						case LIKE_EXACT:
							$value = '^'.$value.'$';
							break;									
					}
					$queries = x_array_set($queries, $criterion->field(), new MongoRegex('/'.$value.'/i'));
					break;
				case BETWEEN:
					$queries = x_array_set($queries, $criterion->field(), array(
						'$gte' => $criterion->value(),
						'$lt' => $criterion->alias(),
					));
					break;
				case IS_NULL:
					$queries = x_array_set($queries, $criterion->field(), array('$exists' => false));
					break;
				case NOT_NULL:
					$queries = x_array_set($queries, $criterion->field(), array('$exists' => true));
					break;
				case EXAMPLE:
					foreach($table->columns() as $key => $column){
						$value = $criterion->value()->get($key);
						if(!is_null($value)){
							$queries = x_array_set($queries, $key, $value);
						}						
					}					
					break;	
				case REGEXP:
					$queries = x_array_set($queries, $criterion->field(), new MongoRegex('/'.$criterion->value().'/i'));
					break;								
			}
		}
		foreach($criterias as $criterion){
			switch($criterion->type()){
				case Criteria_AND:
					$andQueries = $this->translateCriteria($table, $criterion);
					foreach($andQueries as $key => $value){
						$queries = x_array_set($queries, $key, $value);
					}
					break;
				case Criteria_OR:
					$orQueries = $this->translateCriteria($table, $criterion);
					if(!isset($queries['$or'])){						
						$or = $queries;
						$queries = array();
						$queries['$or'] = array($or);
					}
					array_push($queries['$or'], $orQueries);
					break;
			}
		}
		return $queries;
	}
}
?>